package findEnsemble;

import genetic_algorithm.Chromosome;
import genetic_algorithm.Crossover;
import genetic_algorithm.GeneticAlgorithm;
import genetic_algorithm.Mutation;
import genetic_algorithm.Population;
import genetic_algorithm.Selection;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.LinkedList;
import java.util.List;

import mlp.Mlp;
import utils.GAretVal;

/**
 * Runs a genetic algorithm to find optimal ensemble of 10 neural networks (all networks
 * are already trained).
 * ATTENTION: before running, the activation function of mlp.Neuron.java must be set to tanh
 * and mlp.NEGATIVE should be -1
 */
public class FindEnsembleMain {

	private static final int MAX_GENS = 1000; // maximal number of generations to run
	private static final int CROSSOVER_MODE = FindEnsembleCrossover.SINGLE_POINT; // mode for the crossover phase;
	private static final int SELECTION_MODE = FindEnsembleSelection.ROULETTE; // selection phase mode
	private static final int TOURNAMENT_SIZE = 5; // how many chromosomes to include in tournament (if such mode is chosen for selection)
	private static final int MUTATION_MODE = FindEnsembleMutation.SINGLE_DIGIT; // mode for the mutation phase
	private static final int POP_SIZE = 200; // number of chromosomes in the population
	private static final double CROSSOVER_RATE = 0.95; // chance to perform crossover
	private static final double MUTATION_RATE = 0.25; // chance to perform mutation
	public static final String TEST_FILE = "validate1_reduce.csv"; // path to test file
	private static final String MLPS_DIRECTORY = "/home/ofri/Desktop/MLPs/single_digit_36_hidden_27_4"; // path to train file
	private static boolean REMOVE_PARENTS = false; // if chromosomes can be selected only once
	private static final int ELITE_SIZE = 20; // number of elite chromosomes
	
	public static final int NUM_CLASSES = 10;
	
	public static void main(String[] args) {

		// read the serialized neural networks
		List<List<Mlp>> mlps = readMlps();
		
		// allocate objects to implement each phase of the genetic algorithm
		FindEnsembleFitness.initTestData(TEST_FILE);
		FindEnsembleFitness fitnessFunction = new FindEnsembleFitness(mlps);
		Crossover crossover = new FindEnsembleCrossover(CROSSOVER_MODE);
		Selection selector = new FindEnsembleSelection(REMOVE_PARENTS, SELECTION_MODE, TOURNAMENT_SIZE);
		Mutation mutation = new FindEnsembleMutation(mlps.get(0).size(), MUTATION_MODE);
		
		// create initial population
		List<Chromosome> chromList = new LinkedList<Chromosome>();		
		for (int i = 0; i < POP_SIZE; ++i) {
			chromList.add(new FindEnsembleChromosome(mlps.get(0).size()));
		}
		Population population = new Population(chromList, fitnessFunction);
		
		// initialize the genetic algorithm
		GeneticAlgorithm GA = new GeneticAlgorithm(fitnessFunction, crossover,
				selector, mutation, CROSSOVER_RATE, MUTATION_RATE);

		// find optimal solution
		GAretVal result = GA.execute(population, MAX_GENS, true, ELITE_SIZE);

		// TODO DEBUG PRINT!!!
		System.out.println("result chromosome: "
				+ result.getOptSolution().getAllValues());
		
		// generate csv for execution data
		result.createsCsvFiles("find_ensemble.csv"); 
	}
	
	private static List<List<Mlp>> readMlps() {

		List<List<Mlp>> mlps = new LinkedList<List<Mlp>>();
		for (int digit = 0; digit < NUM_CLASSES; ++digit) {
			
			// read all networks in this directory
			File file = new File(MLPS_DIRECTORY + "/" + digit);
			System.out.println("Read the mlp of " + file.getName() + ".");
			List<Mlp> digitMlpsList = new LinkedList<Mlp>();
			for (File mlpFile : file.listFiles()) {
				try {
					System.out.print("|");
					FileInputStream readFile = new FileInputStream(mlpFile);
					ObjectInputStream fileStream = new ObjectInputStream(readFile);
					digitMlpsList.add((Mlp) fileStream.readObject());
					fileStream.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			mlps.add(digitMlpsList);
			System.out.println();
		}
		
		System.out.println("reading mlps completed.");
		return mlps;
	}
}
